<h1 id="u语言词法分析器设计文档">U语言词法分析器设计文档</h1>
<h2 id="前置描述">前置描述</h2>
<p>本文档描述了u语言词法分析器（以下简称lex），用VS Code +
扩展（Markdown Preview Enhanced）编写</p>
<h4 id="最后更新时间-20231030">最后更新时间 ： 2023/10/30</h4>
<h2 id="公开api">公开API</h2>
<p>lex的实现将n行（n&gt;0）的已经去除换行符的字符串做为接收的输入，输出为按行数升序排列好的每行词法单元</p>
<p>lex进行词法分析的公开的API为</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Lex 进行词法分析</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">//   - file是文件名</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">//   - Len是识别行数</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">//   - value是已经分好行的待识别代码,必须已经去除换行符</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">//   - errctx是错误处理上下文</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co">//   - EnableSinLineComments 是否启用多行注释,除非为了格式化文件，否则应该为false</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> Lex<span class="op">(</span>file <span class="dt">string</span><span class="op">,</span> Len <span class="dt">int</span><span class="op">,</span> value <span class="op">[]</span><span class="dt">string</span><span class="op">,</span> errctx <span class="op">*</span>errcode<span class="op">.</span>ErrCtx<span class="op">,</span> EnableSinLineComments <span class="dt">bool</span><span class="op">)</span> <span class="op">*</span>FileToken</span></code></pre></div>
<p>返回值的定义为</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 一个文件的词法分析结果</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> FileToken <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="co">// 文件名</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    File <span class="dt">string</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// 按行数升序排列的每行词法标记</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    Value <span class="op">[]</span>Line</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="co">// 一行的单词标记</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> Line <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// 行数</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    Linenum <span class="dt">int</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">//从开头到结尾的单词标记</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    Value <span class="op">[]</span>Token</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="co">// Token 单词标记 代表一个最小词法单元</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> Token <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>    <span class="co">// 种别码</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>    TYPE TokenType</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>    <span class="co">// 属性值</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>    Value <span class="dt">string</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Token（单词标记） 保存了种别码和属性值<br> Line（一行的单词标记）
保存了一行从开头到结尾的单词标记和行数<br> FileToken
保存了一个文件的词法分析结果，包含文件名，n个一行的单词标记（n&gt;=0）<br></p>
<p>词法分析的过程是</p>
<ol type="1">
<li>创建一个文件的词法分析结果</li>
<li>调用Scan（一行扫描器）分析出一行的所有单词标记</li>
<li>将分析出的一行的所有单词标记保存在一个文件的词法分析结果的Value字段的行数-1偏移量里</li>
<li>重复2-3直到所有行都已经扫描完毕</li>
</ol>
<h2 id="一行扫描器scan">一行扫描器（Scan）</h2>
<p>一行扫描器（Scan）是lex实现词法分析的关键步骤，接收一行的字符串，必须已经去除换行符，输出这一行的所有单词标记</p>
<p>一行扫描器从左到右对字符串进行扫描，按照下列规则进行分析为单词标记</p>
<p>定义下列为分隔符</p>
<ul>
<li>空格</li>
<li>制表符</li>
<li>有语义的符号指<strong>运算符或括号</strong>（例如加号，左小括号，小于号）</li>
</ul>
<p>到扫描发现分隔符时，此时的现状如下图示</p>
<p><img src="发现分隔符.svg" /></p>
<p>对于分隔符，空格和制表符被忽略，有语义的符号除了例外被分析为种别码为<strong>有语义的符号</strong>对应的种别码，属性值为被分析的值的单词标记</p>
<p>例外是 ：</p>
<ul>
<li>// 被视为注释，这个值以及之后的字符串都将被忽略</li>
<li>/* 被视为多行注释开头， */
被视为多行注释结束，之间的字符串都将被忽略</li>
<li>&amp;如果后跟着一个标识符,不单独识别，而是和<strong>分隔符右</strong>一起识别，比如&amp;a被分析为种别码为&amp;对于的种别码,属性值为&amp;a</li>
<li><span class="citation"
data-cites="如果后跟着一个标识符">@如果后跟着一个标识符</span>,不单独识别，而是和<strong>分隔符右</strong>一起识别，比如@a被分析为种别码为&amp;对于的种别码,属性值为@a</li>
<li>” 如果前边是 \ ，转义为字符串中的字符</li>
<li>发现一个”,不单独识别，而是和<strong>分隔符右</strong>一起识别，将一组在左边没有转义的”和右边没有转义的”连同之间的内容一起分析为字符串</li>
<li>如果是and，并且右边没有分隔符，不识别为位与操作符</li>
<li>如果是左中括号，不单独识别，而是<strong>和后面最近的包括自身左右中括号数量相等的字符串一起识别</strong>为，种别码为带中括号的Token，属性值为这个字符串</li>
<li>-如果后面是整数，前面是赋值或逗号或左小括号或相等或不等或if或小于或大于，识别为整数</li>
<li>-如果后面是浮点数，前面是赋值或逗号或左小括号或相等或不等或if或小于或大于，识别为浮点数</li>
</ul>
<p>分隔符左和分隔符右可能为空，如果不为空且没有被上述规则处理，则按下列规则识别为单词标记</p>
<ul>
<li>如果是例如var这种<strong>预定义的关键字</strong>，分析为种别码为对应关键字的种别码，属性值为关键字的值的单词标记</li>
<li>如果是数字，分析为种别码为<strong>整数或浮点数</strong>对应的种别码，属性值为被分析的值的单词标记</li>
<li>如果是布尔值，分析为种别码为<strong>true或false</strong>对应的种别码，属性值为被分析的值的单词标记</li>
<li>如果其他规则不能成功分析出单词标记，分析为符号</li>
<li>如果前面是符号或取地址或不止有中括号的Token，后面是带中括号的Token,合并为不止有中括号的Token</li>
<li>如果前面是带中括号的Token，后面是符号或取地址或不止中括号的Token，合并为不止有中括号的Token</li>
</ul>
<h2 id="附注">附注</h2>
<p>将语言规范描述的索引表达式或数组类型，识别为一个Token是因为构建抽象语法树的解析算法<strong>依赖类型或一个值只有一个Token的假设</strong></p>
